
// Copyright (C) 2010-2011 Luca Piccioni
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//   
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace Derm.Render
{
	/// <summary>
	/// Aggregate resource, which effectively produce rendering result.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The RenderObjectBase class define an object able to execute a single shader program. This mean that RenderObjectBase class define
	/// methods to link the shader program state (uniform and variable) to derived class implementation. The object can be adapted
	/// to support multiple shader programs.
	/// </para>
	/// <para>
	/// Shader program inputs are <see cref="BufferObject"/> instances linked to vertex shader inputs variables. There are different
	/// ways to organize the shader inputs:
	/// - Separate buffers: this mode works by binding each input buffer to its relative shader input. Pros: single input buffers can be
	///   replaced easily. Cons: more buffers, more bindings to setup (this question is almost solved by using <see cref="VertexArrayObject"/>;
	///   multiple inputs reside on sparse memory, decreasing performances (this question is minimized by using few buffer objects).
	/// - Interleaved buffers: this mode combine a buffer object set interleaving each single vertex attribute. Pros: there is only one single
	///   buffer, rendering faster due memory layout. Cons: addition of successive buffer objects is not possible (it has be bound as separate
	///   buffer); remotion of already interleaved buffers is not possible (they shall remain in memory, otherwise interleaved buffers has to
	///   be re-created).
	/// - Packed buffers: this mode combine a buffer object set, packing sequentially the entire buffers; tipically this buffer memory layout
	/// </para>
	/// </remarks>
	public abstract class RenderObjectBase : RenderResource, IRenderObject
	{
		#region Constructors

		/// <summary>
		/// Construct a RenderObjectBase.
		/// </summary>
		protected RenderObjectBase()
		{
			Type thisType = GetType();

			// @todo Execute at each constructor? Cache mapped uniforms and inputs per Type?

			// Input bindings map for this resource
			mReflectedInputs = MapInputs(thisType);
			mHasReflectedInputs = mReflectedInputs.Count > 0;
		}

		#endregion

		#region Generic Bindings

		/// <summary>
		/// Class used for inspect uniform values using reflection.
		/// </summary>
		protected internal class ReflectedBinding
		{
			#region Constructors

			/// <summary>
			/// Construct an uniform binding.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="MemberInfo"/> pointing to the method returning an uniform value.
			/// </param>
			private ReflectedBinding(RenderObjectBindingAttribute attribute, MemberInfo info)
			{
				if (attribute == null)
					throw new ArgumentNullException("attribute");
				if (info == null)
					throw new ArgumentNullException("info");
				
				// Store the member info
				mMemberInfo = info;
			}

			/// <summary>
			/// Construct an uniform binding based on field implementation.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="FieldInfo"/> pointing to the field returning an uniform value.
			/// </param>
			protected ReflectedBinding(RenderObjectBindingAttribute attribute, FieldInfo info) : this(attribute, (MemberInfo)info)
			{
				// Store declared binding type
				mBindingType = info.FieldType;
				// Get delegate
				mGetBindingValue = GetBindingValueByField;
			}

			/// <summary>
			/// Construct an uniform binding based on property implementation.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="PropertyInfo"/> pointing to the property returning an uniform value.
			/// </param>
			protected ReflectedBinding(RenderObjectBindingAttribute attribute, PropertyInfo info) : this(attribute, (MemberInfo)info)
			{
				if (info.CanRead == false)
					throw new ArgumentException("write-only property", "info");

				// Store declared binding type
				mBindingType = info.PropertyType;
				// Get delegate
				mGetBindingValue = GetBindingValueByProperty;
			}

			/// <summary>
			/// Construct an uniform binding based on property implementation.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="PropertyInfo"/> pointing to the property returning an uniform value.
			/// </param>
			protected ReflectedBinding(RenderObjectBindingAttribute attribute, MethodInfo info) : this(attribute, (MemberInfo)info)
			{
				// Store declared binding type
				mBindingType = info.ReturnType;
				// Get delegate
				mGetBindingValue = GetBindingValueByMethod;
			}

			#endregion

			#region Binding Type Management

			/// <summary>
			/// Get the binding type.
			/// </summary>
			/// <returns>
			/// It returns a <see cref="System.Type"/> that specify the declared binding value type. This type is not the actual
			/// one, but the type specified by property or method.
			/// </returns>
			public virtual Type DeclaredBindingType
			{
				get { return (mBindingType); }
			}

			/// <summary>
			/// The declared binding type. This information cannot change over the time.
			/// </summary>
			private readonly Type mBindingType;

			#endregion

			#region Binding Value Management

			/// <summary>
			/// Get the current binding value.
			/// </summary>
			/// <param name="ctx">
			/// A <see cref="RenderContext"/> that specify the render context currently used.
			/// </param>
			/// <param name="container">
			/// A <see cref="System.Object"/> that holds the binding value.
			/// </param>
			/// <returns>
			/// It returns the binding value from <paramref name="container"/>.
			/// </returns>
			public virtual object GetBindingValue(RenderContext ctx, object container)
			{
				Debug.Assert(mGetBindingValue != null);

				return (mGetBindingValue(ctx, container));
			}

			/// <summary>
			/// Reset the cached value.
			/// </summary>
			public void ResetCachedValue()
			{
				mCachedUniformValue = null;
			}

			/// <summary>
			/// Delegate used to get binding value.
			/// </summary>
			/// <param name="ctx">
			/// A <see cref="RenderContext"/> that specify the render context currently used.
			/// </param>
			/// <param name="container">
			/// A <see cref="System.Object"/> that holds the binding value.
			/// </param>
			/// <returns>
			/// It returns the binding value.
			/// </returns>
			private delegate object GetBindingValueDelegate(RenderContext ctx, object container);

			/// <summary>
			/// Delegate used to get binding value.
			/// </summary>
			private readonly GetBindingValueDelegate mGetBindingValue;

			/// <summary>
			/// Cache support by method implementor.
			/// </summary>
			private readonly bool mCacheable;

			/// <summary>
			/// Last binding value.
			/// </summary>
			private object mCachedUniformValue;

			#endregion

			#region Get Binding Implementations

			private object GetBindingValueByField(RenderContext ctx, object container)
			{
				if (ctx == null)
					throw new ArgumentNullException("ctx");
				if (container == null)
					throw new ArgumentNullException("container");

				return (((FieldInfo)mMemberInfo).GetValue(container));
			}

			private object GetBindingValueByProperty(RenderContext ctx, object container)
			{
				if (ctx == null)
					throw new ArgumentNullException("ctx");
				if (container == null)
					throw new ArgumentNullException("container");

				return (((PropertyInfo)mMemberInfo).GetValue(container, null));
			}

			private object GetBindingValueByInstancePropertyDelegate(RenderContext ctx, object container)
			{
				if (ctx == null)
					throw new ArgumentNullException("ctx");
				if (container == null)
					throw new ArgumentNullException("container");

				return (mPropertyDelegate.Invoke());
			}

			private object GetBindingValueByMethod(RenderContext ctx, object container)
			{
				if (ctx == null)
					throw new ArgumentNullException("ctx");
				if (container == null)
					throw new ArgumentNullException("container");
				
				return (((MethodInfo)mMemberInfo).Invoke(container, new object[] { ctx }));
			}

			private object GetBindingValueByInstanceMethodDelegate(RenderContext ctx, object container)
			{
				if (ctx == null)
					throw new ArgumentNullException("ctx");
				if (container == null)
					throw new ArgumentNullException("container");

				return (mMemberDelegate.Invoke(ctx));
			}

			/// <summary>
			/// Common information for getting uniform information from properties and methods.
			/// </summary>
			private readonly MemberInfo mMemberInfo;

			private delegate object InstancePropertyGetDelegate();

			private readonly InstancePropertyGetDelegate mPropertyDelegate;

			private delegate object InstanceMethodDelegate(RenderContext ctx);

			private readonly InstanceMethodDelegate mMemberDelegate;

			#endregion
		}

		#endregion

		#region Uniform Bindings Map

		/// <summary>
		/// This object uniform state.
		/// </summary>
		private readonly ShaderUniformContainerState mUniformState;

		#endregion

		#region Vertex Arrays Inputs

		/// <summary>
		/// 
		/// </summary>
		protected void ResetInputValueCache()
		{
			foreach (ReflectedInputBinding binding in mReflectedInputs.Values)
				binding.ResetCachedValue();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="binding"></param>
		protected void ResetInputValueCache(string binding)
		{
			if (mReflectedInputs.ContainsKey(binding) == false)
				throw new ArgumentException("no binding", "binding");

			mReflectedInputs[binding].ResetCachedValue();
		}

		/// <summary>
		/// Create a map of the input bindings declared by <paramref name="type"/> using reflection.
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		private static Dictionary<string,ReflectedInputBinding> MapInputs(Type type)
		{
			Dictionary<string,ReflectedInputBinding> inputMap = new Dictionary<string,ReflectedInputBinding>();

			if (type == null)
				throw new ArgumentNullException("type");

			// Uniforms specifies as properties
			PropertyInfo[] allProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

			foreach (PropertyInfo propertyInfo in allProperties) {
				Attribute[] attribs = Attribute.GetCustomAttributes(propertyInfo, typeof(RenderObjectInputAttribute), true);

				if ((attribs != null) && (attribs.Length > 0)) {
					// Required checks
					if ((propertyInfo.PropertyType != typeof(BufferObject)) && (propertyInfo.PropertyType.IsSubclassOf(typeof(BufferObject)) == false))
						throw new InvalidOperationException(String.Format("property '{0}.{1}' is not a buffer", type, propertyInfo.Name));

					foreach (Attribute attrib in attribs) {
						RenderObjectInputAttribute inputAttribute = (RenderObjectInputAttribute)attrib;

						if (inputMap.ContainsKey(inputAttribute.BindingName) == true)
							throw new InvalidOperationException(String.Format("{0} has an ambiguos input binding '{1}'", type, inputAttribute.BindingName));

						if (String.IsNullOrEmpty(inputAttribute.BlockName) == false)
							inputMap.Add(String.Format("{0}.{1}", inputAttribute.BlockName, inputAttribute.BindingName), new ReflectedInputBinding(inputAttribute, propertyInfo));
						else
							inputMap.Add(inputAttribute.BindingName, new ReflectedInputBinding(inputAttribute, propertyInfo));
					}
				}
			}

			// Uniforms specifies as methods
			MethodInfo[] allMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);

			foreach (MethodInfo methodInfo in allMethods) {
				Attribute[] attribs = Attribute.GetCustomAttributes(methodInfo, typeof(RenderObjectInputAttribute), true);

				if ((attribs != null) && (attribs.Length > 0)) {
					ParameterInfo[] infoParams = methodInfo.GetParameters();

					// Required checks
					if (methodInfo.ReturnType.IsSubclassOf(typeof(BufferObject)) == false)
						throw new InvalidOperationException(String.Format("{0} input binding '{1}' is not a buffer", type, methodInfo.Name));
					if (infoParams.Length != 1)
						throw new InvalidOperationException(String.Format("{0} input binding '{1}' takes invalid argument count", type, methodInfo.Name));
					if (infoParams[0].ParameterType != typeof(RenderContext))
						throw new InvalidOperationException(String.Format("{0} input binding '{1}' takes invalid argument type (it shall be RenderContext)", type, methodInfo.Name));

					foreach (Attribute attrib in attribs) {
						RenderObjectInputAttribute inputAttribute = (RenderObjectInputAttribute)attrib;

						if (inputMap.ContainsKey(inputAttribute.BindingName) == true)
							throw new InvalidOperationException(String.Format("{0} has an ambiguos input binding '{1}'", type, inputAttribute.BindingName));
						inputMap.Add(inputAttribute.BindingName, new ReflectedInputBinding(inputAttribute, methodInfo));
					}
				}
			}

			return (inputMap);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="binding"></param>
		/// <returns></returns>
		private ReflectedInputBinding GetInputBinding(string binding)
		{
			ReflectedInputBinding reflectedInputBinding;

			mReflectedInputs.TryGetValue(binding, out reflectedInputBinding);

			return (reflectedInputBinding);
		}

		/// <summary>
		/// An input array.
		/// </summary>
		private class InputBinding
		{
			/// <summary>
			/// 
			/// </summary>
			/// <param name="type"></param>
			/// <param name="bufferObject"></param>
			public InputBinding(Primitive type, BufferObject bufferObject)
			{
				mMode = type;
				Array = bufferObject;
			}

			/// <summary>
			/// The input array primitive.
			/// </summary>
			public Primitive Mode { get { return (mMode); } }

			/// <summary>
			/// The input array primitive.
			/// </summary>
			private readonly Primitive mMode;

			/// <summary>
			/// Offset
			/// </summary>
			internal uint Offset;

			/// <summary>
			/// The input array.
			/// </summary>
			private BufferObject Array;
		}

		/// <summary>
		/// Class used for inspect uniform values using reflection.
		/// </summary>
		private class ReflectedInputBinding : ReflectedBinding
		{
			#region Constructors

			/// <summary>
			/// Construct an uniform binding based on property implementation.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="PropertyInfo"/> pointing to the property returning an uniform value.
			/// </param>
			public ReflectedInputBinding(RenderObjectInputAttribute attribute, PropertyInfo info) : base(attribute, info)
			{
				// Store attribute
				mInputAttribute = attribute;
			}

			/// <summary>
			/// Construct an uniform binding based on property implementation.
			/// </summary>
			/// <param name="attribute">
			/// The <see cref="RenderObjectUniformAttribute"/> specified on <paramref name="info"/> declaration.
			/// </param>
			/// <param name="info">
			/// A <see cref="PropertyInfo"/> pointing to the property returning an uniform value.
			/// </param>
			public ReflectedInputBinding(RenderObjectInputAttribute attribute, MethodInfo info) : base(attribute, info)
			{
				// Store attribute
				mInputAttribute = attribute;
			}

			#endregion

			#region Input Attribute

			/// <summary>
			/// Input attribute.
			/// </summary>
			public RenderObjectInputAttribute InputAttribute
			{
				get { return (mInputAttribute); }
			}

			/// <summary>
			/// Input attribute.
			/// </summary>
			private readonly RenderObjectInputAttribute mInputAttribute;

			#endregion
		}

		/// <summary>
		/// The input array configuration.
		/// </summary>
		private List<Dictionary<string,InputBinding>> mInputBindings = new List<Dictionary<string,InputBinding>>();

		/// <summary>
		/// Map between uniform binding name and the member able to specify uniform value.
		/// </summary>
		private readonly Dictionary<string,ReflectedInputBinding> mReflectedInputs;

		/// <summary>
		/// Flags indicating whether this instance has relfected inputs.
		/// </summary>
		private readonly bool mHasReflectedInputs;

		#endregion

		#region Object Rendering

		/// <summary>
		/// ShaderProgram used for rendering.
		/// </summary>
		public ShaderProgram RenderProgram
		{
			get { return (mRenderProgram); }
			set { mRenderProgram = value; }
		}

		/// <summary>
		/// Vertex arrays.
		/// </summary>
		public VertexMatrixObject VertexArrays {  get { return (mVertexMatrix); } }

		/// <summary>
		/// Render this object, after having setup shader program.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for rendering.
		/// </param>
		protected virtual void RenderThis(RenderContext ctx)
		{
			// Issue rendering
			mVertexMatrix.RenderArrays(ctx, RenderProgram);
		}

		/// <summary>
		/// Program used for rendering.
		/// </summary>
		private ShaderProgram mRenderProgram;

		/// <summary>
		/// Vertex arrays.
		/// </summary>
		private readonly VertexMatrixObject mVertexMatrix = new VertexMatrixObject();

		#endregion

		#region RenderResource Overrides

		/// <summary>
		/// RenderResource object class.
		/// </summary>
		internal static readonly Guid AggregateObjectClass = new Guid("C459ABF1-56C6-4ACD-AA4C-B2F5FC9E6210");

		/// <summary>
		/// RenderResource object class.
		/// </summary>
		public override Guid ObjectClass { get { return (AggregateObjectClass); } }

		/// <summary>
		/// Determine whether this object requires a name bound to a context or not.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for creating this object name.
		/// </param>
		/// <returns>
		/// It returns a false, since this is not a real OpenGL object.
		/// </returns>
		protected override bool RequiresName(RenderContext ctx)
		{
			return (false);
		}

		/// <summary>
		/// Actually create this RenderResource resources.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for allocating resources.
		/// </param>
		/// <remarks>
		/// This method actually do:
		/// - Link the shader program
		/// </remarks>
		protected override void CreateObject(RenderContext ctx)
		{
			// Create shader program
			CreateShaderProgram(ctx);
			// Allocate attributes on GPU
			mVertexMatrix.Create(ctx);

			// Check shader availability
			if (RenderProgram == null) {
				// Vertex arrays shall have their own shader
				foreach (VertexArrayObject vbo in mVertexMatrix.Arrays) {
					if (vbo.Shader == null)
						throw new InvalidOperationException("no shader defined");
				}
			}
		}

		private void CreateShaderProgram(RenderContext ctx)
		{
			RenderObjectProgramAttribute programAttribute = (RenderObjectProgramAttribute)Attribute.GetCustomAttribute(GetType(), typeof(RenderObjectProgramAttribute));
			

			if (programAttribute != null) {
				ShaderCompilerContext cctx = new ShaderCompilerContext(ctx.ShadingVersion);

				// Detect shader program defines, based on available inputs
				if (programAttribute.ProgramSymbols != null) {
					foreach (string programSymbol in programAttribute.ProgramSymbols)
						cctx.DefineSymbol(programSymbol);
				}
				// Use cached shader program?
				ShaderCacheService cacheService = ShaderCacheService.GetService(ctx);
				mRenderProgram = cacheService.CreateShaderProgram(ctx, cctx, programAttribute.ProgramName);
			}
			// A shader program is not required, vertex arrays can have their own program
			if (mRenderProgram == null)
				return;
			// Create shader program (if not cached)
			if (mRenderProgram.IsLinked == false)
				mRenderProgram.Create(ctx);
		}

		#endregion

		#region IRenderObject Implementation

		/// <summary>
		/// Render this object.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="RenderContext"/> used for rendering.
		/// </param>
		public virtual void Render(RenderContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");
			if (ctx.IsCurrent() == false)
				throw new ArgumentException("not current", "ctx");

			if (mRenderProgram != null) {
				// Use shader program
				mRenderProgram.Bind(ctx);
				// Set uniform values specified by this object
				if (mUniformState != null) {
					mUniformState.CreateState(ctx, mRenderProgram);
					mUniformState.ApplyState(ctx, mRenderProgram);
				}
				ctx.CurrentState.Apply(ctx, mRenderProgram);
			}

			// Render this object
			RenderThis(ctx);
		}

		#endregion
	}

	/// <summary>
	/// Common implementation of a RenderObjectBase.
	/// </summary>
	public abstract class RenderObject : RenderObjectBase
	{
		#region Common Transform State Management

		/// <summary>
		/// This RenderObject model matrix.
		/// </summary>
		[RenderObjectUniform("ds_Model")]
		public Matrix4x4 GetModel(RenderContext ctx)
		{
			return (ctx.Model);
		}

		/// <summary>
		/// This RenderObject projection matrix.
		/// </summary>
		[RenderObjectUniform("ds_Projection")]
		public Matrix4x4 GetProjection(RenderContext ctx)
		{
			return (ctx.Projection);
		}

		/// <summary>
		/// This RenderObject model matrix multiplied with projection matrix.
		/// </summary>
		[RenderObjectUniform("ds_ModelProjection")]
		public Matrix4x4 GetModelProjection(RenderContext ctx)
		{
			return (ctx.Model * ctx.Projection);
		}

		#endregion
	}

	/// <summary>
	/// Attribute used to specify a property or method used for getting an shader program uniform value.
	/// </summary>
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
	public abstract class RenderObjectBindingAttribute : Attribute
	{
		/// <summary>
		/// Construct a RenderResourceUniformAttribute.
		/// </summary>
		/// <param name="binding">
		/// A <see cref="System.String"/> that specify the uniform binding name.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="binding"/> is null or equals to <see cref="System.String.Empty"/>.
		/// </exception>
		protected RenderObjectBindingAttribute(string binding)
		{
			if (String.IsNullOrEmpty(binding) == true)
				throw new ArgumentException("not valid", "binding");

			mBindingName = binding;
		}

		/// <summary>
		/// The uniform binding name.
		/// </summary>
		public string BindingName { get { return (mBindingName); } }

		/// <summary>
		/// The uniform binding name.
		/// </summary>
		private readonly string mBindingName;
	}

	/// <summary>
	/// Attribute used to specify a property or method used for getting an shader program uniform value.
	/// </summary>
	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
	public sealed class RenderObjectUniformAttribute : RenderObjectBindingAttribute
	{
		/// <summary>
		/// Construct a RenderResourceUniformAttribute.
		/// </summary>
		/// <param name="binding">
		/// A <see cref="System.String"/> that specify the uniform binding name.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="binding"/> is null or equals to <see cref="System.String.Empty"/>.
		/// </exception>
		public RenderObjectUniformAttribute(string binding) : base(binding)
		{

		}
	}

	/// <summary>
	/// Attribute used to specify a property or method used for getting an shader program uniform value.
	/// </summary>
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
	public sealed class RenderObjectInputAttribute : RenderObjectBindingAttribute
	{
		/// <summary>
		/// Construct a RenderResourceInputAttribute.
		/// </summary>
		/// <param name="binding">
		/// A <see cref="System.String"/> that specify the input binding name.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="binding"/> is null or equals to <see cref="System.String.Empty"/>.
		/// </exception>
		public RenderObjectInputAttribute(string binding) : base(binding)
		{
			
		}

		/// <summary>
		/// Block containing the input.
		/// </summary>
		public string BlockName;

		/// <summary>
		/// A preprocessor symbol defined in the case this input is valid.
		/// </summary>
		public string ProgramSymbol;
	}

	/// <summary>
	/// Attribute used to specify a shader program for the underlying RenderObjectBase class.
	/// </summary>
	[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
	public sealed class RenderObjectProgramAttribute : Attribute
	{
		/// <summary>
		/// Construct a RenderObjectProgramAttribute.
		/// </summary>
		/// <param name="program">
		/// A <see cref="System.String"/> that specify the program name.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="program"/> is null or equals to <see cref="System.String.Empty"/>.
		/// </exception>
		public RenderObjectProgramAttribute(string program)
		{
			if (String.IsNullOrEmpty(program))
				throw new ArgumentException("not valid", "program");

			mProgramLibraryId = program;
		}

		/// <summary>
		/// Construct a RenderObjectProgramAttribute.
		/// </summary>
		/// <param name="program">
		/// A <see cref="System.String"/> that specify the program name.
		/// </param>
		/// <param name="symbols">
		/// A <see cref="System.String"/> that specify the program compilation preprocessor symbols, separated by commas.
		/// </param>
		/// <exception cref="ArgumentException">
		/// Exception thrown if <paramref name="program"/> is null or equals to <see cref="System.String.Empty"/>.
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown if <paramref name="symbols"/> is null.
		/// </exception>
		public RenderObjectProgramAttribute(string program, string symbols) : this(program)
		{
			if (symbols == null)
				throw new ArgumentNullException("symbols");

			// Determine program compilation symbols
			mProgramSymbols = symbols.Split(new char[] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
		}

		/// <summary>
		/// The shader program name.
		/// </summary>
		public string ProgramName { get { return (mProgramLibraryId); } }

		/// <summary>
		/// The shader program compilation symbols.
		/// </summary>
		public IEnumerable<string>  ProgramSymbols { get { return (mProgramSymbols); } }

		/// <summary>
		/// The shader program name.
		/// </summary>
		private readonly string mProgramLibraryId;

		/// <summary>
		/// The shader program name.
		/// </summary>
		private readonly string[] mProgramSymbols;
	}
}